﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;
using Workflows.Components.TextMining.Properties;

namespace Workflows.Components.TextMining.Clustering
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class UnsupervisedClusteringComponent:WorkflowComponentBase
    {
        #region props
        private UnsupervisedClusteringInstruction _Instruction;
        public override InstructionBase Instruction
        {
            get { return _Instruction; }
            set { _Instruction=(UnsupervisedClusteringInstruction)value; }
        }

        private UnsupervisedClusteringOutput _Output;
        public override ResultBase Output
        {
            get { return _Output; }
            set { _Output =(UnsupervisedClusteringOutput)value; }
        }

        /// <summary>
        /// <Docs>
        ///     <Doc DocID="0">
        ///         <Term TermID="0" Phrase=""/>
        ///     </Doc>
        /// </Docs>
        /// </summary>
        public string DocTermListFilePath
        {
            get
            {
                return this._Instruction.DocTermListFilePath;
            }
            set
            {
                this._Instruction.DocTermListFilePath = value;
            }
        }

        /// <summary></summary>
        public double DistanceThreshold
        {
            get
            {
                return this._Instruction.DistanceThreshold;
            }
            set
            {
                this._Instruction.DistanceThreshold = value;
            }
        }

        /// <summary></summary>
        public double TermCoverageThreshold
        {
            get
            {
                return this._Instruction.TermCoverageThreshold;
            }
            set
            {
                this._Instruction.TermCoverageThreshold = value;
            }
        }

        /// <summary></summary>
        public double TermFreqThreshold
        {
            get
            {
                return this._Instruction.TermFreqThreshold;
            }
            set
            {
                this._Instruction.TermFreqThreshold = value;
            }
        }

        /// <summary></summary>
        public List<string> ExcludedTerms
        {
            get
            {
                return this._Instruction.ExcludedTerms;
            }
            set
            {
                this._Instruction.ExcludedTerms = value;
            }
        }

        /// <summary></summary>
        public DistanceCalculationMethod DistanceMethod
        {
            get
            {
                return this._Instruction.DistanceMethod;
            }
            set
            {
                this._Instruction.DistanceMethod = value;
            }
        }

        /// <summary></summary>
        public PurityMeasureMethod PurityMethod
        {
            get
            {
                return this._Instruction.PurityMethod;
            }
            set
            {
                this._Instruction.PurityMethod = value;
            }
        }

        /// <summary></summary>
        public DocumentClusteringMethod ClusterMethod
        {
            get
            {
                return this._Instruction.ClusterMethod;
            }
            set
            {
                this._Instruction.ClusterMethod = value;
            }
        }

        /// <summary></summary>
        public string ClusteringResultFilePath
        {
            get
            {
                return this._Instruction.ClusteringResultFilePath;
            }
            set
            {
                this._Instruction.ClusteringResultFilePath = value;
            }
        }

        /// <summary></summary>
        public bool CanOverlap
        {
            get
            {
                return this._Instruction.CanOverlap;
            }
            set
            {
                this._Instruction.CanOverlap = value;
            }
        }

        /// <summary>range/distance ratio, default to 1.50</summary>
        public double ReassociationDistanceRange
        {
            get
            {
                return this._Instruction.ReassociationDistanceRange;
            }
            set
            {
                this._Instruction.ReassociationDistanceRange = value;
            }
        }

        /// <summary></summary>
        public TermFuzzyMatchMethod TermFuzzyMatch
        {
            get
            {
                return this._Instruction.TermFuzzyMatch;
            }
            set
            {
                this._Instruction.TermFuzzyMatch = value;
            }
        }
        #endregion

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public UnsupervisedClusteringComponent():base()
        {
            this.ComponentActivityType = WorkflowActivityType.SimpleActivity;
            this.ComponentCategory = WorkflowComponentCategory.TextMining.ToString();
            this.ComponentIcon = Resources.cryptui_3418;
            this.ComponentImage = Resources.doboz;
            this.ComponentName = "Unsupervised Doc Cluster";
            this.Description = "Classify document based on shared term usage";

            this._Instruction = new UnsupervisedClusteringInstruction(
                this.ComponentID, Guid.Empty,
                string.Empty, 0.35, 0.25, 0.05,
                new List<string>(),
                PurityMeasureMethod.EntropyMeasure,
                DistanceCalculationMethod.CosineDistance,
                DocumentClusteringMethod.KMeans, false, 1.50,
                string.Empty, TermFuzzyMatchMethod.None);
            this._Output = new UnsupervisedClusteringOutput(
                this.ComponentID, Guid.Empty,
                string.Empty, 0);
        }
        #endregion

        public override WorkflowComponentBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                UnsupervisedClusteringComponent wfComponent = base.Instantiate(xNode) as UnsupervisedClusteringComponent;

                wfComponent.DocTermListFilePath = XmlDataUtil.GetAttributeValue(xNode, "DocTermListFilePath", "");
                wfComponent.TermCoverageThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "TermCoverageThreshold", "0.25"));
                wfComponent.DistanceThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "DistanceThreshold", "0.35"));
                wfComponent.TermFreqThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "TermFreqThreshold", "0.05"));

                wfComponent.PurityMethod =
                    (PurityMeasureMethod)
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "PurityMethod",
                                                            ((int)PurityMeasureMethod.EntropyMeasure).ToString()));
                wfComponent.DistanceMethod =
                    (DistanceCalculationMethod)
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "DistanceMethod",
                                                            ((int)DistanceCalculationMethod.CosineDistance).ToString()));
                wfComponent.ClusterMethod =
                    (DocumentClusteringMethod)
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "ClusterMethod",
                                                            ((int) DocumentClusteringMethod.KMeans).ToString()));

                wfComponent.ClusteringResultFilePath = XmlDataUtil.GetAttributeValue(xNode, "ClusteringResultFilePath",
                                                                                     "");
                wfComponent.CanOverlap = bool.Parse(XmlDataUtil.GetAttributeValue(xNode, "CanOverlap", false.ToString()));
                wfComponent.ReassociationDistanceRange =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "ReassociationRange", "1.50"));
                wfComponent.TermFuzzyMatch =
                    (TermFuzzyMatchMethod)int.Parse(XmlDataUtil.GetAttributeValue(
                                                         xNode, "TermFuzzyMatch",
                                                         ((int)TermFuzzyMatchMethod.None).
                                                             ToString()));

                XmlNodeList excludedTermNodes = xNode.SelectNodes("ExcludedTerms/ExcludedTerm");
                if (excludedTermNodes != null && excludedTermNodes.Count > 0)
                {
                    wfComponent.ExcludedTerms = new List<string>();
                    foreach (XmlNode excludedTermNode in excludedTermNodes)
                    {
                        wfComponent.ExcludedTerms.Add(XmlDataUtil.GetAttributeValue(excludedTermNode, "Term", ""));
                    }
                }

                return wfComponent;
            }
            else
            {
                throw new Exception("Unable to instantiate component: invalid type specified in xml node");
            }
        }

        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = base.Serialize(ref xDoc, ref parentNode);

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "DocTermListFilePath", this.DocTermListFilePath);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TermCoverageThreshold", this.TermCoverageThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "DistanceThreshold", this.DistanceThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TermFreqThreshold",this.TermFreqThreshold.ToString());

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "PurityMethod", ((int)this.PurityMethod).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "DistanceMethod", ((int)this.DistanceMethod).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ClusterMethod",((int)this.ClusterMethod).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ClusteringResultFilePath", this.ClusteringResultFilePath);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode,"CanOverlap",this.CanOverlap.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ReassociationRange",
                                        this.ReassociationDistanceRange.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TermFuzzyMatch", ((int)this.TermFuzzyMatch).ToString());

            if (this.ExcludedTerms != null && this.ExcludedTerms.Count > 0)
            {
                XmlNode exTermRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "ExcludedTerms");
                foreach (string exTerm in this.ExcludedTerms)
                {
                    XmlNode exTermNode = XmlDataUtil.AddElement(ref xDoc, exTermRootNode, "ExcludedTerm");
                    XmlDataUtil.UpdateAttribute(ref xDoc, exTermNode, "Term", exTerm);
                }
            }

            return xNode;
        }
    }
}
